#include "Game.h"
#include "GameStates\LevelState.h"
#include <ctime>

#ifdef _DEBUG
	#include <iostream>
#endif

// Singleton Access
Game* Game::GetInstance()
{
	static Game instance;
	return &instance;
}

Game::Game()
{
	this->d3d = NULL;
	this->texManager = NULL;
	this->dirInput = NULL;
	this->fModManager = NULL;

	this->screenWidth = 0;
	this->screenHeight = 0;
	this->isWindowed = false;

	this->currTimeStamp = 0;
	this->prevTimeStamp = 0;
	this->currElapsedTime = 0.0f;
	this->totalElapsedTime = 0.0f;

	currLevel = new LevelState();
}

Game::~Game()
{

}

// Init the game class
void Game::Init(HWND _hWnd, HINSTANCE _hInstance, int _screenWidth, int _screenHeight, bool _isWindowed, bool _isVsync)
{
	// Seed the random number generator
	srand((unsigned int)time(0));

	// Set the fields
	isWindowed = _isWindowed;
	screenWidth = _screenWidth;
	screenHeight = _screenHeight;
	d3d			= CSGD_Direct3D::GetInstance();
	texManager	= CSGD_TextureManager::GetInstance();
	dirInput	= CSGD_DirectInput::GetInstance();
	fModManager	= CSGD_FModManager::GetInstance();

	// Init the systems
	d3d->InitDirect3D(_hWnd, _screenWidth, _screenHeight, _isWindowed, _isVsync);
	texManager->InitTextureManager(d3d->GetDirect3DDevice(), d3d->GetSprite());
	dirInput->InitDirectInput(_hWnd, _hInstance, DI_KEYBOARD | DI_MOUSE);
	fModManager->InitFModManager(_hWnd);

	// Start with the Main Menu State
	PushState(currLevel);

	// Direct3d Screen Parameters
	d3d->ChangeDisplayParam(screenWidth, screenHeight, isWindowed);

	// Set initial cursor position
	cursorPos.x = screenWidth * 0.5f;
	cursorPos.y = screenHeight * 0.5f;
}

// Run the game class
bool Game::Main()
{
	// Track time
	currTimeStamp = GetTickCount();
	currElapsedTime = (currTimeStamp - prevTimeStamp) / 1000.0f;	// Turn it into seconds
	totalElapsedTime += currElapsedTime;
	prevTimeStamp = currTimeStamp;

	// Get input
	if (!Input())
		return false;

	// Update objects
	Update();

	// Draw
	Render();

	// Continue the program
	return true;
}

// Shut down the game class
void Game::Exit()
{
	// Clean up the states
	ClearStates();

	if (currLevel)
	{
		delete currLevel;
		currLevel = NULL;
	}

	/* Shut down the systems */
	if(fModManager)
	{
		fModManager->ShutdownFModManager();
		fModManager = NULL;
	}

	if(dirInput)
	{
		dirInput->ShutdownDirectInput();
		dirInput = NULL;
	}

	if(texManager)
	{
		texManager->ShutdownTextureManager();
		texManager = NULL;
	}

	if(d3d)
	{
		d3d->ShutdownDirect3D();
		d3d = NULL;
	}
}

bool Game::Input()
{
	// Get device info
	dirInput->ReadDevices();

	if ((dirInput->KeyDown(DIK_LALT) || dirInput->KeyDown(DIK_RALT)) && dirInput->KeyPressed(DIK_RETURN))
	{
		// Change the windowed flag
		isWindowed = !isWindowed;

		// Change the d3d parameters and clear it out
		d3d->ChangeDisplayParam(screenWidth, screenHeight, isWindowed);
		d3d->Clear();

		// Don't read any more input this frame
		return true;
	}

	if (gameStates[gameStates.size() - 1]->Input())
	{
		cursorPos.x += dirInput->MouseMovementX();
		cursorPos.y += dirInput->MouseMovementY();

		if(cursorPos.x <= 25)
			cursorPos.x = 25;
		else if(cursorPos.x >= screenWidth - 25)
			cursorPos.x = screenWidth - 25;
		if(cursorPos.y <= 24 )
			cursorPos.y = 24;
		else if (cursorPos.y >= screenHeight -24)
			cursorPos.y = screenHeight - 24;
	}
	else
		return false;	// Quit the program

	// Continue the program
	return true;
}

void Game::Update()
{
	// Update the state
	gameStates[gameStates.size() - 1]->Update(currElapsedTime);

	// Update sounds
	fModManager->Update();
}

void Game::Render()
{
	d3d->Clear(128, 128, 128);
	d3d->DeviceBegin();
	d3d->SpriteBegin();

	for (unsigned int i = 0; i < gameStates.size(); ++i)
	{
		// Draw the state
		gameStates[i]->Render();

		// Flush out the device
		d3d->GetSprite()->Flush();
	}

	d3d->SpriteEnd();
	d3d->DeviceEnd();
	d3d->Present();
}

void Game::PushState(BaseState* _state)
{
	// Error Check
	if(_state)
	{
		// Add the new state to the stack
		gameStates.push_back(_state);

		// Enter the new state
		gameStates[gameStates.size() - 1]->Enter();
	}
}

void Game::PopState()
{
	// Exit the current state
	gameStates[(gameStates.size() - 1)]->Exit();

	// Kill the current state
	gameStates.pop_back();
}

void Game::ChangeState(BaseState* _state)
{
	// Empty the stack
	ClearStates();

	// Error check
	if(_state)
	{
		// Add the new state
		PushState(_state);
	}
}

void Game::ClearStates()
{
	// Is the stack empty?
	if(gameStates.size())
	{
		// Loop through all states on the stack
		for(unsigned int i = 0 ; i < gameStates.size() ;++i)
		{
			// Exit the state
			gameStates[i]->Exit();
		}

		// Clear the vector
		gameStates.clear();
	}
}